module mxu_mat_ctrl (
    input logic                                  clk,
    input logic                                  rst_n,
          mxu_cfg_if.mat_ctrl_in                 u_mxu_cfg_if,
          lmb_dat_if.in                          u_lmb_dat_if,
          rmb_dat_if.in                          u_rmb_dat_if,
          pmb_dat_if.in                          u_pmb_dat_if,
          mxu_psb_dat_if.in                      u_psb_dat_if,
          mxu_mat_ctrl_mat_array_if.mat_ctrl_out u_mxu_mat_ctrl_mat_array_if
);

    //  完成一次接受指令的过程
    logic cfg_rdy, cfg_vld;
    logic        last_req_in_instr;
    logic        recv_data_succeed;
    logic [15:0] m1_idx;
    logic [15:0] n1_idx;
    logic [15:0] k1_idx;
    logic [15:0] slice_m1;
    logic [15:0] slice_n1;
    logic [15:0] slice_k1;

    assign cfg_vld  = ~cfg_rdy;
    assign slice_m1 = (u_mxu_cfg_if.slice_m + `M0 - 1) / `M0;
    assign slice_n1 = (u_mxu_cfg_if.slice_n + `N0 - 1) / `N0;
    assign slice_k1 = u_mxu_cfg_if.slice_k1;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cfg_rdy <= 1'b1;
        end else if (cfg_rdy == 1'b0) begin
            if (lst_req_in_instr && recv_data_succeed) begin
                cfg_rdy <= 1'b1;
            end
        end else begin
            if (u_mxu_cfg_if.vld) begin
                cfg_rdy <= 1'b0;
            end
        end
    end

    //上游数据已经准备好
    logic bias_dat_vld;
    logic psum_dat_vld;
    logic dat_vld;
    always @(*) begin
        bias_dat_vld = u_mxu_cfg_if.bias_en ? u_pmb_dat_if.vld : 1'b1;
        psum_dat_vld = u_mxu_cfg_if.psum_en ? u_psb_dat_if.vld : 1'b1;
        dat_vld      = u_lmb_dat_if.vld && u_rmb_dat_if.vld && bias_dat_vld && psum_dat_vld;
    end

    //下游数据可以接收
    logic lmb_dat_rdy;
    logic rmb_dat_rdy;
    logic pmb_dat_rdy;
    logic psb_dat_rdy;
    logic dat_rdy;
    always @(*) begin
        lmb_dat_rdy = u_mxu_mat_ctrl_mat_array_if.rdy;
        rmb_dat_rdy = u_mxu_mat_ctrl_mat_array_if.rdy;
        pmb_dat_rdy = u_mxu_cfg_if.bias_en ? (u_mxu_mat_ctrl_mat_array_if.rdy && (k1_idx == 1'b0)) : 1'b0; //这里为啥不使能的时候恒为0，那后续的dat_rdy岂不是一致为0
        psb_dat_rdy = u_mxu_cfg_if.psum_en ? (u_mxu_mat_ctrl_mat_array_if.rdy && (k1_idx == 1'b0)) : 1'b0;  //同上
        dat_rdy = lmb_dat_rdy && rmb_dat_rdy && pmb_dat_rdy && psb_dat_rdy;
    end

    assign u_lmb_dat_if.rdy  = lmb_dat_rdy;
    assign u_rmb_dat_if.rdy  = rmb_dat_rdy;
    assign u_pmb_dat_if.rdy  = pmb_dat_rdy;
    assign u_psb_dat_if.rdy  = psb_dat_rdy;

    // 成功完成了一笔（发数据&收数据）
    assign recv_data_succeed = dat_vld && dat_rdy;

    // 维护 m1_idx、n1_idx、k1_idx
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            m1_idx <= 16'd0;
            n1_idx <= 16'd0;
            k1_idx <= 16'd0;
        end else if (recv_data_succeed) begin
            if (lst_req_in_instr) begin
                m1_idx <= 16'd0;
                n1_idx <= 16'd0;
                k1_idx <= 16'd0;
            end else if (k1_idx == (slice_k1 - 1)) begin
                k1_idx <= 16'd0;
                if (n1_idx == (slice_n1 - 1)) begin
                    n1_idx <= 16'd0;
                    m1_idx <= m1_idx + 16'd1;
                end else begin
                    n1_idx <= n1_idx + 16'd1;
                end
            end else begin
                k1_idx <= k1_idx + 16'd1;
            end
        end
    end

    assign last_req_in_instr = (m1_idx == (slice_m1 - 1)) && (n1_idx == (slice_n1 - 1)) && (k1_idx == (slice_k1 - 1));

    fp32_t c[`M0-1:0][`N0-1:0];
    always_ff @(*) begin
        for (integer i = 0; i < `M0; i = i + 1) begin
            for (integer j = 0; j < `N0; j = j + 1) begin
                if (u_mxu_cfg_if.psum_en) begin
                    c[i][j] = u_psb_dat_if.dat[i][j];
                end else begin
                    c[i][j] = u_pmb_dat_if.dat[j];  //相当于对bias进行了Broadcast
                end
            end
        end
    end

    logic [1:0] mode;
    always_ff @(*) begin
        if (k1_idx == 1'b0) begin
            if (u_mxu_cfg_if.bias_en || u_mxu_cfg_if.psum_en) begin
                mode = 2'b01;  // a*b+c
            end else begin
                mode = 2'b00;  // a*b
            end
        end else begin
            mode = 2'b10;  // a*b+psum
        end
    end

    assign u_mxu_mat_ctrl_mat_array_if.a      = u_lmb_dat_if.dat;
    assign u_mxu_mat_ctrl_mat_array_if.b      = u_rmb_dat_if.dat;
    assign u_mxu_mat_ctrl_mat_array_if.c      = c;
    assign u_mxu_mat_ctrl_mat_array_if.mode   = mode;
    assign u_mxu_mat_ctrl_mat_array_if.last_k = (k1_idx == (slice_k1 - 1));
    assign u_mxu_mat_ctrl_mat_array_if.vld    = dat_vld;

endmodule
